package com.me.common.util.io;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import sun.misc.BASE64Encoder;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ME
 * @date 2020/11/19
 */
public class ImageUtils {

    static  {
        System.load("E:/Soft/opencv_460/build/java/x64/opencv_java460.dll");
    }

    /**
     * 通过 url下载文件到本地
     */
    public static void download(String urlStr, String localDir, String fileName) throws Exception {
        if (StringUtils.isEmpty(fileName)) {
            fileName = getNameFromUrl(urlStr);
        }

        URL url = new URL(urlStr);
        File file = new File(localDir + File.separator + fileName);
        FileUtils.copyURLToFile(url, file);
    }
    public static void download22(String urlStr, String localDir, String fileName) throws Exception {
        if (StringUtils.isEmpty(fileName)) {
            fileName = getNameFromUrl(urlStr);
        }

        // 构造URL
        URL url = new URL(urlStr);
        // 打开连接
        URLConnection con = url.openConnection();
        // 输入流
        InputStream is = con.getInputStream();
        // 1K的数据缓冲
        byte[] bs = new byte[1024];
        // 读取到的数据长度
        int len;
        // 输出的文件流
        String filename = localDir + File.separator + fileName;  //下载路径及下载图片名称
        File file = new File(filename);
        FileOutputStream os = new FileOutputStream(file, true);
        // 开始读取
        while ((len = is.read(bs)) != -1) {
            os.write(bs, 0, len);
        }
        System.out.println(fileName);
        // 完毕，关闭所有链接
        os.close();
        is.close();
    }

    /**
     * 截取 url 的最后一段作为文件名
     *
     * @param url
     * @return
     */
    public static String getNameFromUrl(String url){
        String name = System.currentTimeMillis() + ".X";
        int index = url.lastIndexOf("/");
        if(index > 0){
            name = url.substring(index + 1);
            if(name.trim().length()>0){
                return name;
            }
        }
        return name;
    }

    /**
     * 将文件读取并转化为Base64字符串
     * @param fileName 文件名
     * @return  Base64字符串
     * @throws Exception
     */
    public static String changeToBase64(String fileName) throws Exception{
        File file = new File(fileName);
        FileInputStream inputFile = new FileInputStream(file);
        byte[] buffer = new byte[(int) file.length()];
        inputFile.read(buffer);
        inputFile.close();
        String base64Code = new BASE64Encoder().encode(buffer);
        return base64Code;
    }

    /**
     * 读取图片
     * @param imagePath
     * @return
     * @throws IOException
     */
    public static BufferedImage readBufferImage(String imagePath) throws IOException {
        return ImageIO.read(new File(imagePath));
    }

    /**
     * 写文件
     * @param imagePath
     * @return
     * @throws IOException
     */
    public static void writeImage(BufferedImage image, String imagePath) throws IOException {
        ImageIO.write(image, "png", new File(imagePath));
    }

    /**
     * 矩形裁剪，设定起始位置，裁剪宽度，裁剪长度（裁剪范围需小于等于图像范围）
     *
     * @param image         原始图片
     * @param xCoordinate
     * @param yCoordinate
     * @param width
     * @param height
     * @return
     */
    public static BufferedImage imageCutByRectangle(BufferedImage image, double xCoordinate, double yCoordinate, double width,
                                                    double height, boolean isSaveSubImage) {
        int imageWidth = image.getWidth();
        int imageHeight = image.getHeight();
        // BufferedImage subImage = image.getSubimage(xCoordinate, yCoordinate, width, height);
        BufferedImage subImage = image.getSubimage((int) xCoordinate, (int) yCoordinate, (int) width, (int) height);
        // 写入文件
        if (isSaveSubImage) {
            try {
                ImageIO.write(subImage, "png", new File("C:\\Users\\bin.yin\\Desktop\\img\\slider.png"));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return subImage;
    }

    /**
     * 获取验证码需要移动的距离
     * @param bigImagePath      大图像Url
     * @param smallBgImagePath  小图像url
     * @param top  需要剪裁的top值
     * @return
     * @throws IOException
     */
    public int getDistance(String bigImagePath, String smallBgImagePath, int top) throws IOException {

        File bFile = new File(bigImagePath);
        File sFile = new File(smallBgImagePath);

        BufferedImage bgBI = ImageIO.read(bFile);
        BufferedImage sBI = ImageIO.read(sFile);

        // 裁剪
        bgBI = bgBI.getSubimage(320, top, bgBI.getWidth() - 330, sBI.getHeight());
        ImageIO.write(bgBI, "png", new File("C:\\Users\\bin.yin\\Desktop\\img\\bg444.png"));
        Mat s_mat = Imgcodecs.imread(sFile.getPath());
        Mat b_mat = Imgcodecs.imread(bFile.getPath());
        // 转灰度图像
        Mat s_newMat = new Mat();
        Imgproc.cvtColor(s_mat, s_newMat, Imgproc.COLOR_BGR2GRAY);

        // 二值化图像
        this.binaryzation(s_newMat);
        Imgcodecs.imwrite("C:\\Users\\bin.yin\\Desktop\\img\\sli444.png", s_newMat);

        Mat s_bnewMat = new Mat();
        Imgproc.cvtColor(b_mat, s_bnewMat, Imgproc.COLOR_BGR2GRAY);
        //Imgcodecs.imwrite(sFile.getPath(), s_newMat);
        // 二值化图像
        this.binaryzation(s_bnewMat);
        Imgcodecs.imwrite("C:\\Users\\bin.yin\\Desktop\\img\\bg445.png", s_bnewMat);

        int result_rows = b_mat.rows() - s_mat.rows() + 1;
        int result_cols = b_mat.cols() - s_mat.cols() + 1;
        Mat g_result = new Mat(result_rows, result_cols, CvType.CV_32FC1);
        Imgproc.matchTemplate(b_mat, s_mat, g_result, Imgproc.TM_SQDIFF); // 归一化平方差匹配法
        // 归一化相关匹配法
        Core.normalize(g_result, g_result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
        Point matchLocation;
        Core.MinMaxLocResult mmlr = Core.minMaxLoc(g_result);
        matchLocation = mmlr.maxLoc; // 此处使用maxLoc还是minLoc取决于使用的匹配算法
        Imgproc.rectangle(b_mat, matchLocation,
                new Point(matchLocation.x + s_mat.cols(), matchLocation.y + s_mat.rows()), new Scalar(0, 255, 0, 0));
        Imgcodecs.imwrite("C:\\Users\\bin.yin\\Desktop\\img\\bg446.png", b_mat);
        System.out.println(((matchLocation.x + s_mat.cols() + 320 +24 - sBI.getWidth()) / 2));
        return (int) Math.round(((matchLocation.x + s_mat.cols() + 320+24 - sBI.getWidth()) / 2));
    }

    /**
     * 图片亮度调整
     *
     * @param image
     * @param param
     * @throws IOException
     */
    public void bloding(BufferedImage image, int param) throws IOException {
        if (image == null) {
            return;
        }

        int rgb, R, G, B;
        for (int i = 0; i < image.getWidth(); i++) {
            for (int j = 0; j < image.getHeight(); j++) {
                rgb = image.getRGB(i, j);
                R = ((rgb >> 16) & 0xff) - param;
                G = ((rgb >> 8) & 0xff) - param;
                B = (rgb & 0xff) - param;
                rgb = ((clamp(255) & 0xff) << 24) | ((clamp(R) & 0xff) << 16) | ((clamp(G) & 0xff) << 8) | ((clamp(B) & 0xff));
                image.setRGB(i, j, rgb);

            }
        }
    }

    // 判断a,r,g,b值，大于256返回256，小于0则返回0,0到256之间则直接返回原始值
    private int clamp(int rgb) {
        if (rgb > 255)
            return 255;
        if (rgb < 0)
            return 0;
        return rgb;
    }

    /**
     * 二值化
     *
     * @param mat
     */
    public void binaryzation(Mat mat) {
        int BLACK = 0;
        int WHITE = 255;
        int ucThre = 0, ucThre_new = 127;
        int nBack_count, nData_count;
        int nBack_sum, nData_sum;
        int nValue;
        int i, j;
        int width = mat.width(), height = mat.height();
        // 寻找最佳的阙值
        while (ucThre != ucThre_new) {
            nBack_sum = nData_sum = 0;
            nBack_count = nData_count = 0;

            for (j = 0; j < height; ++j) {
                for (i = 0; i < width; i++) {
                    nValue = (int) mat.get(j, i)[0];

                    if (nValue > ucThre_new) {
                        nBack_sum += nValue;
                        nBack_count++;
                    } else {
                        nData_sum += nValue;
                        nData_count++;
                    }
                }
            }
            nBack_sum = nBack_sum / nBack_count;
            nData_sum = nData_sum / nData_count;
            ucThre = ucThre_new;
            ucThre_new = (nBack_sum + nData_sum) / 2;
        }
        // 二值化处理
        int nBlack = 0;
        int nWhite = 0;
        for (j = 0; j < height; ++j) {
            for (i = 0; i < width; ++i) {
                nValue = (int) mat.get(j, i)[0];
                if (nValue > ucThre_new) {
                    mat.put(j, i, WHITE);
                    nWhite++;
                } else {
                    mat.put(j, i, BLACK);
                    nBlack++;
                }
            }
        }
        // 确保白底黑字
        if (nBlack > nWhite) {
            for (j = 0; j < height; ++j) {
                for (i = 0; i < width; ++i) {
                    nValue = (int) (mat.get(j, i)[0]);
                    if (nValue == 0) {
                        mat.put(j, i, WHITE);
                    } else {
                        mat.put(j, i, BLACK);
                    }
                }
            }
        }
    }

    /**
     * 获取需要滑动的轨迹
     *
     * @param distance 需要滑动的距离
     * @return
     */
    private List<Integer> getTrack(int distance) {
        List<Integer> tracks = new ArrayList<Integer>();
        //当前位移
        double current = 0;
        int actionMove = 0;
        //减速阀值
        double mid = distance * 4 / 5;
        //计算间隔
        double t = 0.2;
        //除速度
        double v = 0;
        //加速度
        double a = 0;

        while (current < distance) {
            if (current < mid) {
                a = 4;
            } else {
                a = -6;
            }
            double v0 = v;
            v = v0 + a * t;
            double move = v0 * t + 1 / 2 * a * t * t;
            current = current + move;

            actionMove = actionMove + (int) Math.round(move);
            tracks.add((int) Math.round(move));
        }

        //因为4舍5入过，所有，需要校准偏差值
        if (distance != actionMove) {
            tracks.add(actionMove - distance);
        }

        return tracks;
    }

}
